#include "../include/ThreadPool.hpp"
#include "../include/Log.hpp"
#include "../include/Socket.hpp"
#include "../include/MySQL.hpp"
#include <iostream>
#include <sys/epoll.h>
#include <set>
#include <sstream>
#include <signal.h>

void Catch_ctrl_c(int sig);

class ChatRoomServer
{
private:
    ys::LogFile logfile;        // 日志文件
    ys::TcpServer tcp_server;   // 服务端对象
    ys::ThreadPool thread_pool; // 线程池对象
    const size_t MAXENENTS;     // epoll一次能返回的最大的事件数
    
    std::set<int> set_connfd;   // bao'c已连接的客户端容器
    int epollfd;                // epollfd
    // 锁
    std::mutex set_lock;

public:
    friend void Catch_ctrl_c(int sig);

    // 构造函数和析构函数
    ChatRoomServer(const size_t threads = 5, const size_t maxenents = 10)
        : thread_pool(threads), MAXENENTS(maxenents), epollfd(-1)
    {
    }
    ~ChatRoomServer()
    {
        if (epollfd != -1)
        {
            close(epollfd);
        }
    }

    // 初始化服务端
    bool InitServer(const char *ip, const unsigned int port) { return tcp_server.InitServer(ip, port); }
    // 初始化日志文件
    bool InitLogFile(const char *filename, std::ios::openmode openmode = std::ios::app, bool bBackup = true, bool bEnbuffer = false, const size_t MaxLogSize = 100)
    {
        logfile.m_MaxLogSize = MaxLogSize;
        return logfile.Open(filename, openmode, bBackup, bEnbuffer);
    }
    // 启动服务器
    void runServer()
    {
        // 创建一个 epoll 描述符
        epollfd = epoll_create(1);

        // 添加监听描述符事件
        struct epoll_event ev;
        ev.data.fd = tcp_server._listen_sockfd;
        ev.events = EPOLLIN;                                               // 读事件
        epoll_ctl(epollfd, EPOLL_CTL_ADD, tcp_server._listen_sockfd, &ev); // 添加fd和对应的事件

        while (1)
        {
            struct epoll_event events[MAXENENTS]; // 存放发生事件的结构数组

            // 等待监视的 socket 有事件发生
            int infds = epoll_wait(epollfd, events, MAXENENTS, -1); // 阻塞等待
            // 返回失败
            if (infds < 0)
            {
                perror("epoll_wait()");
                break;
            }
            else if (infds == 0)
            {
                printf("epoll_wait() timeout.\n");
                continue;
            }

            // 遍历所有发生事件的结构数组
            for (int i = 0; i < infds; ++i)
            {
                // 如果是监听套接字的读事件
                if ((events[i].data.fd == tcp_server._listen_sockfd) && (events[i].events & EPOLLIN))
                {
                    if (tcp_server.Accept() == false)
                    {
                        printf("accept() failed.\n");
                        continue;
                    }

                    // 把新的客户端添加到 epoll 中
                    memset(&ev, 0, sizeof(struct epoll_event));
                    ev.data.fd = tcp_server._client_connfd;
                    ev.events = EPOLLIN;
                    epoll_ctl(epollfd, EPOLL_CTL_ADD, tcp_server._client_connfd, &ev);

                    logfile.Write(tcp_server._client_connfd, "connected.");

                    continue;
                }
                else if (events[i].events & EPOLLIN)
                {
                    // 客户端有数据过来或客户端的socket连接被断开
                    char buffer[1024];
                    memset(buffer, 0, sizeof(buffer));

                    int buffer_size = 0;
                    int sockfd = events[i].data.fd;
                    ys::TcpRead(sockfd, buffer, &buffer_size);

                    if (buffer_size <= 0)
                    {
                        logfile.Write(events[i].data.fd, "disconnected.");
                        close(sockfd);
                        continue;
                    }

                    // 解析字符串
                    int cmd = -1;
                    ys::GetStrFromXML(buffer, "cmd", cmd);
                    std::string message;
                    std::string name;
                    int colorInd;
                    switch (cmd)
                    {
                    // 注册账号
                    case 0:
                    {
                        ys::GetStrFromXML(buffer, "message", message);
                        thread_pool.enqueue(&ChatRoomServer::Register, this, message, sockfd);
                        break;
                    }
                    // 登陆
                    case 1:
                    {
                        ys::GetStrFromXML(buffer, "message", message);
                        thread_pool.enqueue(&ChatRoomServer::LogIN, this, message, sockfd);
                        break;
                    }
                    // 发信息
                    case 2:
                    {
                        ys::GetStrFromXML(buffer, "message", message);
                        ys::GetStrFromXML(buffer, "name", name);
                        ys::GetStrFromXML(buffer, "color", colorInd);
                        thread_pool.enqueue(&ChatRoomServer::broadcastMessage, this, name, message, colorInd, sockfd);
                        break;
                    }
                    // 退出登陆
                    case 3:
                    {
                        thread_pool.enqueue(&ChatRoomServer::LogOUT, this, sockfd);
                        break;
                    }

                    // 其他
                    default:
                    {
                        close(sockfd);
                        break;
                    }
                    }

                    continue;
                }
            }
        }

        close(epollfd);
        epollfd = -1;
        return;
    }

private:
    // 接收并广播信息
    void broadcastMessage(const std::string &name, const std::string &str, int colorIndex, int sockfd)
    {
        logfile.Write("[", name, "] say: [", str, "]");
        // 形成信息
        std::string message(str);
        ys::FormXML(message, "message");
        std::string message_name(name);
        ys::FormXML(message_name, "name");

        std::string data("4"); // 发送信息是 4 cmd
        ys::FormXML(data, "code");
        data.append(message_name);
        data.append("<color>");
        data.append(std::to_string(colorIndex));
        data.append("</color>");
        data.append(message); // 形成 xml

        std::unique_lock<std::mutex> lk(set_lock);
        for (const auto &connfd : set_connfd)
        {
            if (connfd == sockfd)
                continue; // 不广播给自己
            ys::TcpWrite(connfd, data.c_str(), data.size());
        }
        return;
    }
    // 注册操作
    void Register(const std::string &str, int sockfd)
    {
        if (str.size() == 0)
        {
            // LI::TcpWrite(sockfd, "<code>0</code><message>Register Failed.</message>");
            ys::TcpWrite(sockfd, "<code>0</code>");
            return;
        }

        // 在用户信息文件添加用户
        int pos = str.find(' ');
        // 反馈信息
        UserSQL insert_obj;
        if (insert_obj.AddUser(str.substr(0, pos).c_str(), str.substr(pos + 1).c_str()) == true)
        {
            ys::TcpWrite(sockfd, "<code>1</code>");
        }
        else
        {
            ys::TcpWrite(sockfd, "<code>0</code>");
        }

        return;
    }
    // 登陆操作
    void LogIN(const std::string &str, int sockfd)
    {
        if (str.size() == 0)
        {
            // LI::TcpWrite(sockfd, "<code>2</code><message>LogIN Failed.</message>");
            ys::TcpWrite(sockfd, "<code>2</code>");
            return;
        }
        int pos = str.find(' ');
        std::string name = str.substr(0, pos);
        std::string InPassword = str.substr(pos + 1);
        // 查找用户名
        UserSQL search_obj;
        std::string password = search_obj.SearchUser(name.c_str());
        // 密码正确
        if (password.size() > 0 && password == InPassword)
        {
            {
                std::unique_lock<std::mutex> lk(set_lock); // 上锁
                set_connfd.insert(sockfd);                 // 把 sockfd 插入 set
            }
            // LI::TcpWrite(sockfd, "<code>0</code><message>LogIN Success.</message>");
            ys::TcpWrite(sockfd, "<code>3</code>");
            logfile.Write(name, " enter the chat room");
            return;
        }

        ys::TcpWrite(sockfd, "<code>2</code>");
        return;
    }
    // 退出登陆操作
    void LogOUT(int sockfd)
    {
        {
            std::unique_lock<std::mutex> lk(set_lock); // 上锁
            set_connfd.erase(sockfd);                  // 将 sockfd 删除
        }
        return;
    }
};

// 定义全局的服务端对象
std::shared_ptr<ChatRoomServer> crs_ptr = std::make_shared<ChatRoomServer>();

int main(int argc, char const *argv[])
{
    // 判断命令行参数是否输入正确
    if (argc != 3)
    {
        std::cout << "No ip and port" << std::endl;
        std::cout << "Using example: ./chatRoomServer 0.0.0.0 8082 " << std::endl;
        return -1;
    }
    // 定义信号的处理方式
    signal(SIGINT, Catch_ctrl_c);  // ctrl + c
    signal(SIGTERM, Catch_ctrl_c); // 终止信号--系统关机、重启

    crs_ptr->InitLogFile("../log/test.log", std::ios::app);
    // 初始化服务器
    crs_ptr->InitServer(argv[1], atoi(argv[2]));
    // 启动服务器
    crs_ptr->runServer();

    return 0;
}

// 捕获信号
void Catch_ctrl_c(int sig)
{
    if (crs_ptr->epollfd != -1)
    {
        close(crs_ptr->epollfd);
    }

    crs_ptr->logfile.Close();

    exit(sig);
}